package com.qinyu.treehole.service.Impl;

import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qinyu.treehole.bo.RegisterUserBo;
import com.qinyu.treehole.bo.UpdateUserBo;
import com.qinyu.treehole.entities.R;
import com.qinyu.treehole.entities.User;
import com.qinyu.treehole.mapper.UserMapper;
import com.qinyu.treehole.service.EmailService;
import com.qinyu.treehole.service.FeedService;
import com.qinyu.treehole.service.UserService;
import com.qinyu.treehole.tool.TokenTool;
import com.qinyu.treehole.tool.UserTool;
import com.qinyu.treehole.vo.*;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DigestUtils;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate jsonRedisTemplate;
    @Autowired
    private EmailService emailService;
    @Autowired
    private FeedService feedService;
    @Autowired
    private UserMapper userMapper;

    private final Object lock = new Object();

    @Override
    public AutoLoginUserVo parseUserToAutoLoginUser(User user) {
        AutoLoginUserVo autoLoginUser = new AutoLoginUserVo();
        autoLoginUser.setName(user.getName());
        autoLoginUser.setAvatar(user.getAvatar());
        return autoLoginUser;
    }

    @Override
    public User getUserByUserName(String userName) {
        User user = getOne(new QueryWrapper<User>().eq("name", userName));
        return user;
    }


    @Override
    public boolean isPasswordValid(String psd, String md5_psd) {
        String md5_password = DigestUtils.sha1DigestAsHex(psd);
        return StringUtils.equals(md5_password, md5_psd);
    }

    @Override
    public LoginUserVo parseUserToLoginUserVo(User user, String token) {
        LoginUserVo loginUserVo = new LoginUserVo();
        loginUserVo.setId(user.getId());
        loginUserVo.setName(user.getName());
        loginUserVo.setAvatar(user.getAvatar());
        loginUserVo.setToken(token);
        return loginUserVo;
    }

    @Override
    public String setTokenForLoginUser(Integer userId) {
        // 获取ops
        HashOperations<String, String, String> ops = stringRedisTemplate.opsForHash();
        String id = String.valueOf(userId);
        // 如果用户已有token，尝试更新时间
        String tokenFromUserId = ops.get(TokenTool.TOKEN_NAME, id);
        if(null != tokenFromUserId){
            ops.put(TokenTool.TOKEN_TIME_NAME, tokenFromUserId, String.valueOf(System.currentTimeMillis()));
            return tokenFromUserId;
        }

        // 随机获取token
        String token = UUID.randomUUID().toString();

        // 为userId设置token
        ops.put(TokenTool.TOKEN_NAME, id, token);

        // 为token设置有效期
        ops.put(TokenTool.TOKEN_TIME_NAME, token, String.valueOf(System.currentTimeMillis()));

        return token;
    }

    @Override
    public void exitLoginUser(Integer userId) {
        HashOperations<String, String, String> ops = stringRedisTemplate.opsForHash();

        String id = String.valueOf(userId);
        String token = ops.get(TokenTool.TOKEN_NAME, id);

        // 清除token
        ops.delete(TokenTool.TOKEN_NAME, id);
        ops.delete(TokenTool.TOKEN_TIME_NAME, token);
    }

    @Override
    public User getUserByUserEmail(String email) {
        User user = getOne(new QueryWrapper<User>().eq("email", email));
        return user;
    }

    @Override
    public String getActiveCodeAndSetUser(RegisterUserBo registerUserBo) {
        ValueOperations ops = jsonRedisTemplate.opsForValue();

        String key =  UserTool.ACTIVE_NAME;
        String code;
        do{
            code = RandomStringUtils.randomAlphanumeric(6);
        }while(jsonRedisTemplate.hasKey(key + code));

        key += code;
        ops.set(key, registerUserBo, 5, TimeUnit.MINUTES);

        emailService.sendActiveHtmlMail(registerUserBo.getEmail(), code);

        return code;
    }

    @Override
    public R activeAndRegisterUser(String code) {
        ValueOperations ops = jsonRedisTemplate.opsForValue();
        String key = UserTool.ACTIVE_NAME + code;
        Object obj = ops.get(key);

        if(obj instanceof RegisterUserBo){
            RegisterUserBo registerUserBo = (RegisterUserBo)obj;

            // 注册用户
            User user = new User();
            user.setName(registerUserBo.getName());
            user.setEmail(registerUserBo.getEmail());
            String password = registerUserBo.getPassword();
            user.setPassword(DigestUtils.sha1DigestAsHex(password));
            user.setGender(registerUserBo.getGender());

            boolean save = false;
            // 并发情况下，需要加上锁
            synchronized(lock){
                // 保证不存在
                User userByUserName = getUserByUserName(registerUserBo.getName());
                if(null != userByUserName){
                    return R.no(null, UserTool.USER_NAME_EXIST);
                }
                User userByUserEmail = getUserByUserEmail(registerUserBo.getEmail());
                if(null != userByUserEmail){
                    return R.no(null, UserTool.USER_EMAIL_EXIST);
                }

                save = save(user);
            }

            if(save) return R.ok(null, UserTool.USER_ACTIVE_SUCCESS);
        }

        return R.no(null, UserTool.USER_ACTIVE_FAIL);
    }

    @Override
    public UserSetVo parseUserToUserSetVo(User user) {
        UserSetVo userSetVo = new UserSetVo();
        userSetVo.setUserId(user.getId());
        userSetVo.setName(user.getName());
        userSetVo.setAvatar(user.getAvatar());
        userSetVo.setGender(user.getGender());
        userSetVo.setIntroduction(user.getIntroduction());
        return userSetVo;
    }

    @Override
    public boolean updateUserByUpdateUserBo(UpdateUserBo updateUserBo) {
        User user = getById(updateUserBo.getUserId());

        user.setName(updateUserBo.getName());
        user.setAvatar(updateUserBo.getAvatar());
        user.setGender(updateUserBo.getGender());
        user.setIntroduction(updateUserBo.getIntroduction());

        return updateById(user);
    }

    @Override
    public UserIndexVo getUserIndexVoByUser(User user) {
        UserIndexVo vo = new UserIndexVo();
        vo.setUserId(user.getId());
        vo.setName(user.getName());
        vo.setAvatar(user.getAvatar());
        vo.setGender(user.getGender());
        vo.setIntroduction(user.getIntroduction());

        Long feedSize = feedService.getUserFeedCountByUserId(user.getId());
        vo.setFeedCount(feedSize);

        String suffix = String.valueOf(user.getId());

        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        Long fansSize = ops.size(UserTool.FANS_NAME+suffix);
        Long followsSize = ops.size(UserTool.FOLLOW_NAME+suffix);

        vo.setFansCount(fansSize);
        vo.setFollowsCount(followsSize);

        return vo;
    }

    @Override
    public UserIndexVo getUserIndexVoByUserLogin(User user, Integer userId) {
        UserIndexVo vo = new UserIndexVo();
        vo.setUserId(user.getId());
        vo.setName(user.getName());
        vo.setAvatar(user.getAvatar());
        vo.setGender(user.getGender());
        vo.setIntroduction(user.getIntroduction());

        Long feedSize = feedService.getUserFeedCountByUserId(user.getId());
        vo.setFeedCount(feedSize);

        String suffix = String.valueOf(user.getId());

        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        Long fansSize = ops.size(UserTool.FANS_NAME+suffix);
        Long followsSize = ops.size(UserTool.FOLLOW_NAME+suffix);

        vo.setFansCount(fansSize);
        vo.setFollowsCount(followsSize);

        // 判断是否关注
        Boolean isFllow = ops.isMember(UserTool.FANS_NAME + user.getId(), userId);
        vo.setIsFollow(isFllow);

        return vo;
    }

    @Override
    public UserIndexVo getUserIndexVoBySelfUserLogin(User user) {
        UserIndexVo vo = new UserIndexVo();
        vo.setUserId(user.getId());
        vo.setName(user.getName());
        vo.setAvatar(user.getAvatar());
        vo.setGender(user.getGender());
        vo.setIntroduction(user.getIntroduction());

        Long feedSize = feedService.getUserFeedCountBySelfUserId(user.getId());
        vo.setFeedCount(feedSize);

        String suffix = String.valueOf(user.getId());

        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        Long fansSize = ops.size(UserTool.FANS_NAME+suffix);
        Long followsSize = ops.size(UserTool.FOLLOW_NAME+suffix);

        vo.setFansCount(fansSize);
        vo.setFollowsCount(followsSize);

        return vo;
    }

    private List<RelationUserVo> getRelationUserVosByUserId(Integer userId, String key){
        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        return ops.members(key).stream().map(id->{
            User user = getById(id);

            RelationUserVo vo = new RelationUserVo();
            vo.setUserId(id);
            vo.setUname(user.getName());
            vo.setAvatar(user.getAvatar());
            vo.setIntroduction(user.getIntroduction());

            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RelationUserVo> getFansRelationUserVosByUserId(Integer userId) {
        return getRelationUserVosByUserId(userId, UserTool.FANS_NAME+userId);
    }

    @Override
    public List<RelationUserVo> getFollowsRelationUserVosByUserId(Integer userId) {
        return getRelationUserVosByUserId(userId, UserTool.FOLLOW_NAME+userId);
    }

    private List<RelationLoginUserVo> getRelationLoginUserVosByUserId(Integer id, String key){
        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        return ops.members(key).stream().map(uid->{
            User user = getById(uid);

            RelationLoginUserVo vo = new RelationLoginUserVo();
            vo.setUserId(uid);
            vo.setUname(user.getName());
            vo.setAvatar(user.getAvatar());
            vo.setIntroduction(user.getIntroduction());

            vo.setIsFollow(ops.isMember(UserTool.FANS_NAME+uid, id));

            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RelationLoginUserVo> getFansRelationLoginUserVosByUserId(Integer id, Integer userId) {
        return getRelationLoginUserVosByUserId(id, UserTool.FANS_NAME+userId);
    }

    @Override
    public List<RelationLoginUserVo> getFollowsRelationLoginUserVosByUserId(Integer id, Integer userId) {
        return getRelationLoginUserVosByUserId(id, UserTool.FOLLOW_NAME+userId);
    }

    @Override
    public List<RelationUserVo> searchAllUserByName(String name) {
        return userMapper.searchUserByName(name).stream().map(id->{
            User user = getById(id);

            RelationUserVo vo = new RelationUserVo();
            vo.setUserId(id);
            vo.setUname(user.getName());
            vo.setAvatar(user.getAvatar());
            vo.setIntroduction(user.getIntroduction());

            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<RelationLoginUserVo> searchAllUserLoginByName(String name, Integer userId) {
        SetOperations<String, Integer> ops = jsonRedisTemplate.opsForSet();
        return userMapper.searchUserByName(name).stream().map(uid->{
            User user = getById(uid);

            RelationLoginUserVo vo = new RelationLoginUserVo();
            vo.setUserId(uid);
            vo.setUname(user.getName());
            vo.setAvatar(user.getAvatar());
            vo.setIntroduction(user.getIntroduction());

            vo.setIsFollow(ops.isMember(UserTool.FANS_NAME+uid, userId));

            return vo;
        }).collect(Collectors.toList());
    }
}
